import { Inject, Injectable, LoggerService } from '@nestjs/common';
import { FixedThreadPool } from 'poolifier';
const chalk = require('chalk');
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import * as dayjs from 'dayjs';

@Injectable()
export class AppService {
  private readonly SYNC_LABEL = '-Sync';
  private pool: FixedThreadPool;
  private callback: Function;
  private resolveMap = new Map();
  private fno = 0;
  private log = true;

  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: LoggerService,
  ) {
    this.pool = new FixedThreadPool(1, __dirname + '/lib/worker.js', {
      messageHandler: (res: {
        funcName: string;
        fno: number;
        data: object;
      }) => {
        const { funcName, fno, data } = res;
        this.logMsgReceived(fno, funcName, data);
        if (this.isSync(funcName)) {
          const key = this.formatKey(funcName, fno);
          this.resolveMap.get(key)(data);
          this.resolveMap.delete(key);
        } else {
          this.callback?.(res);
        }
      },
    });

    // this.test();
  }

  async initMemBridge(
    capacity: number = 1024,
    dllPath?: string,
    dllName?: string,
  ) {
    await this.pool.execute({ capacity, dllPath, dllName }, 'initMemBridge');
  }

  async writeMsg(funcName: string, data: any) {
    const fno = this.getFno();
    this.logMsgToSend(fno, funcName, data);
    if (this.isSync(funcName)) {
      return new Promise((resolve, reject) => {
        const key = this.formatKey(funcName, fno);
        this.resolveMap.set(key, resolve);
        this._writeMsg(funcName, fno, data);
      });
    } else {
      await this._writeMsg(funcName, fno, data);
    }
  }

  on(callback: Function) {
    this.callback = callback;
  }

  openLog() {
    this.log = true;
  }

  closeLog() {
    this.log = false;
  }

  private async _writeMsg(funcName: string, fno: number, data: any) {
    await this.pool.execute({ funcName, fno, data }, 'writeMsg');
  }

  // 判断上报的消息是否为同步函数调用对应的消息
  private isSync(funcName: string) {
    return funcName.endsWith(this.SYNC_LABEL);
  }

  // 获取发送序号
  private getFno() {
    const fno = this.fno;
    this.fno++;
    this.fno %= 10_000;
    return fno;
  }

  private formatKey(funcName: string, fno: number) {
    return `${funcName}-${fno}`;
  }

  private logMsgToSend(fno: number, funcName: string, data: object) {
    if (this.log) {
      const time = dayjs();
      console.log(`${chalk.red.bold('****************************************')}
${chalk.red.bold('发送消息')}: ${chalk.cyan(this.formatTimestamp(time))}
${chalk.red.bold('fno     ')}: ${chalk.yellow.bold(fno)}
${chalk.red.bold('funcName')}: ${chalk.blue.bold(funcName)}
${chalk.red.bold('data    ')}: ${chalk.greenBright.bold(JSON.stringify(data, null, 2))}
${chalk.red.bold('****************************************')}`);
      const msg = { type: '发送消息', fno, funcName, data };
      this.logger.warn(msg);
    }
  }

  private logMsgReceived(fno: number, funcName: string, data: object) {
    if (this.log) {
      const time = dayjs();
      console.log(`${chalk.cyan.bold('****************************************')}
${chalk.cyan.bold('收到消息')}: ${chalk.cyan(this.formatTimestamp(time))}
${chalk.cyan.bold('fno     ')}: ${chalk.yellow.bold(fno)}
${chalk.cyan.bold('funcName')}: ${chalk.blue.bold(funcName)}
${chalk.cyan.bold('data    ')}: ${chalk.greenBright.bold(JSON.stringify(data, null, 2))}
${chalk.cyan.bold('****************************************')}`);
      const msg = { type: '收到消息', fno, funcName, data };
      this.logger.warn(msg);
    }
  }

  private formatTimestamp(time: dayjs.Dayjs) {
    return time.format('YYYY-MM-DD HH:mm:ss.SSS');
  }

  test() {
    this.initMemBridge(1000);
    this.on((msg) => {});
    setInterval(async () => {
      const res = await this.writeMsg('testFunc-Sync', { msg: '你好, world!' });
    }, 1000);
  }
}
